#ifndef _qf_oled_h
#define _qf_oled_h
#ifdef __cplusplus
extern "C"
{
#endif

    /*
        Chinese is encoded in Unicode. If it is garbled, please change the encoding method of the editor.
        单色OLED库 By启凡科创 适用于任意支持C语言的平台

        此库绘图效率较低（相对于裸奔绘图），但是灵活性非常高（任意位置均能绘制），
        暂不准备提供绘制变量等操作（因为推荐重定向打印单个字符oled_draw_char()到格式化输出printf函数进行数据打印）

        特色：
            1）任意位置绘制中英文、图片（包括超出屏幕的区域自动截断）
            2）任意设置绘制的区域，超出区域内的内容自动截断
            3）字体的打印，字间距、行间距可设置
            4）字体大小为比例放大缩小，一个16*16中文字模即可打印16*16、32*32、64*64等按照整数比例放大的大小
            5）超级轻量，如128*64分辨率屏仅不到1.2k的RAM和不到10k的flash即可全功能运行
            6）移植方便，仅需更改部分内容如 驱动型号、工作平台、接口类型、发送数据的接口函数宏定义即可立即开启流畅UI之旅

        移植步骤：
            1）OLED_MODULE_EN为1启用编译，不用时置为0屏蔽模块节省资源
            2）根据编码环境更改汉字编码定义选项
            3）驱动型号尺寸更改为自己使用中的型号
            4）配置OLED_WRITE_MODE选项以选择使用IIC驱动还是SPI驱动
            5）如果驱动方式为SPI，则配置对应的DC等引脚电平设置定义接口
            6）将格式符合的SPI、IIC总线发送一个字节、指定长度字节函数配置到定义
                且在配置之前，应当已经在靠前部分包含对应的头文件
            7）可根据自己需求配置OLED初始化后的屏幕亮度Brightness
            8）oled_work_on_c51 此参数根据平台进行更改，否则很可能在C51内存溢出

        至此，移植已经完全结束，可以用以下示例代码测试是否能正常驱动
        oled_begin();
        oled_draw_str("Hello world!",1);
        oled_push_screen();

        如出现无法驱动等问题，可以检查对应的SPI、IIC总线是否配置正确，用到的相关IO是否在使用前已经初始化为输出模式
        还有，总线速度也有一定的影响，可以先用一个比较低的总线速率：IIC-100k、SPI-500k进行初步测试成功后再加快速率

        添加BMP位图请到QF_OLED_BMP.c内查看相关说明
        添加中文字体请到QF_OLED_FONF.h内查看相关说明

        version:v2.1.0  2022-11-7
    */

#include "LIB_CONFIG.h" //QF TEK开发框架配置文件，第三方移植请注释
    // #include <WIRE.h>       //如果使用IIC驱动则应包含对应的IIC驱动头文件
    //  #include <SPI.h> //如果使用SPI驱动则应包含对应的SPI驱动头文件

    // 必要依赖库
    // #include <stdint.h>//报错请注释

#ifndef OLED_LIB_EN
#define OLED_LIB_EN 0 // OLED模块使能，0禁用编译，1允许编译使用
#endif

#if OLED_LIB_EN

#define oled_work_on_c51 1 // 工作平台选择，1是带xdata的C51，0是常规ram
#if oled_work_on_c51
#define font_ram_place code
#else
#define font_ram_place
#endif

#define oled_chinese_

// 驱动芯片选择  不用的注释掉
// #define SSD1306_12832
#define SSD1306_12864 // SSD1315兼容
// #define SH1106_12864

/*
    注意：
        必须实现相应的总线发送数据的底层函数然后在下面宏定义进行绑定，否则无法正常工作！
*/
#define OLED_WRITE_MODE 1 // 1:IIC  0:SPI

#if (OLED_WRITE_MODE == 0) // 使用SPI通信时务必修改,未使用的引脚删去括号并留空
#define DC_SET (P16 = 1)   // SPI通信时设置DC为高电平的函数
#define DC_CLR (P16 = 0)   // SPI通信时设置DC为低电平的函数
#define CS_SET (P54 = 1)   // SPI通信时设置CS为高电平的函数
#define CS_CLR (P54 = 0)   // SPI通信时设置CS为低电平的函数
#define RES_SET (P17 = 1)  // SPI通信时设置RES为高电平的函数
#define RES_CLR (P17 = 0)  // SPI通信时设置RES为低电平的函数
#endif

// 屏幕默认参数
#define oled_slave_addr 0x78 // 屏幕IIC地址 
#define Brightness 127       // 屏幕初始化亮度0-255

// 以下IIC或SPI发送函数宏定义请务必修改！！！
#if OLED_WRITE_MODE
////////////////////////////////////IIC基础函数参数/////////////////////////////////////////////
/*
    后面括号内为IIC发送发送指定长度字节函数，格式如下
    如：iic_write_bytes(uint8_t slave,uint8_t reg,uint8_t *dat,uint16_t size)
        slave:  屏幕设备地址，
        reg:    IIC写寄存器位置，
        *dat:   发送的数据的首地址
        size:   发送的字节数
*/
#define oled_iic_write_bytes(slave, reg, dat, size) (wire_fast_write_bytes(slave, reg, dat, size))
/////////////////////////////////////////////////////////////////////////////////////////
#else
// ////////////////////////////////////SPI基础函数参数/////////////////////////////////////////////

/*
    后面括号内为SPI发送发送指定长度字节函数，格式如下
    如：spi_write_bytes(uint8_t *dat,uint16_t size)
        *dat:   发送的数据的首地址
        size:   发送的字节数
*/
void spi_write_bytes(uint8_t *dat,uint16_t size);
#define oled_spi_write_bytes(dat, size) (spi_write_bytes(dat, size))
    /////////////////////////////////////////////////////////////////////////////////////////

#endif

    typedef enum
    {
        inverse,          // 反色 /反向显示
        usual,            // 不反色 /正向显示
        auto_line_enable, // 自动换行
        auto_line_disable // 不换行
    } oled_display_type;  // 屏幕显示类型

    /**
     * @brief 初始化OLED屏
     */
    void oled_begin(void);

    /**
     * @brief 设置屏幕亮度
     *
     * @param val 0-255
     */
    void oled_set_light(uint8_t val);

    /**
     * @brief 设置显示方向
     *
     * @param mode
     * @param ··inverse 反向显示
     * @param ··usual 正向显示(默认)
     */
    void oled_set_rotation(oled_display_type mode);

    /**
     * @brief 设置屏幕显示
     *
     * @param state 1开启，0关闭
     */
    void oled_set_power(uint8_t state);

    /**
     * @brief 刷新指定区域缓存内容到屏幕
     *
     * @param x 左上角起始坐标
     * @param y 左上角起始坐标
     * @param width 宽度
     * @param height 高度
     */
    void oled_push_area(int16_t x, int16_t y, uint8_t width, uint8_t height);

    /**
     * @brief 刷新全屏缓存内容到屏幕
     */
    void oled_push_screen(void);

    /**
     * @brief 设置允许绘制的缓存区域
     *
     * @param start_x 起始左上角坐标
     * @param start_y 起始左上角坐标
     * @param stop_x 终点右下角坐标
     * @param stop_y 终点右下角坐标
     */
    void oled_set_area(uint8_t start_x, uint8_t start_y, uint8_t stop_x, uint8_t stop_y);

    /**
     * @brief 设置允许绘制的缓存区域
     *
     * @param start_x 起始左上角坐标
     * @param start_y 起始左上角坐标
     * @param width 宽度
     * @param height 高度
     */
    void oled_set_area_size(uint8_t start_x, uint8_t start_y, uint8_t width, uint8_t height);

    /**
     * @brief 设置字间距、行间距
     *
     * @param x_space 字间距
     * @param y_space 行间距
     */
    void oled_set_print_space(uint8_t x_space, uint8_t y_space);

    /**
     * @brief 设置中英文打印字体缩放比例，最小为1
     *
     * @param size 对应英文关系：高度 = size * 8，对应中文关系：宽高 = size * 8,因基础中文大小为16*16，
     *   所以其奇数 * 8并无实际效果， 如英文字号2高度为16，中文的1、2都为16
     */
    void oled_set_font_size(uint8_t size);

    /**
     * @brief 设置光标位置
     *
     * @param x
     * @param y
     */
    void oled_set_position(int16_t x, int16_t y);

    /**
     * @brief 设置自动换行功能使能
     *
     * @param type
     * @param ··auto_line_enable 自动换行
     * @param ··auto_line_disable 不换行(默认)
     */
    void oled_set_auto_line(oled_display_type type);

    /**
     * @brief 在缓存指定位置绘制点
     *
     * @param x
     * @param y
     * @param mode 1：白色，0：黑色
     */
    void oled_draw_pix(int16_t x, int16_t y, uint8_t mode);

    /**
     * @brief 在缓存指定位置绘制指定宽高线（可用作绘制实心矩形）
     *
     * @param x
     * @param y
     * @param width 宽
     * @param height 高
     * @param mode 1：白色，0：黑色
     */
    void oled_draw_line(int16_t x, int16_t y, uint8_t width, uint8_t height, uint8_t mode);

    /**
     * @brief 在缓存指定位置绘制指定区域线（可用作绘制实心矩形）
     *
     * @param start_x 起始左上角坐标
     * @param start_y 起始左上角坐标
     * @param stop_x 终点右下角坐标
     * @param stop_y 终点右下角坐标
     * @param mode 1：白色，0：黑色
     */
    void oled_fill_box(int16_t start_x, int16_t start_y, int16_t stop_x, int16_t stop_y, uint8_t mode);

    /**
     * @brief 在缓存光标处绘制一个字符
     *
     * @param chr 字符
     * @param mode 1：白色，0：黑色
     */
    void oled_draw_char(char chr, uint8_t mode);

    /**
     * @brief 在缓存光标处绘制中英文字符串
     *
     * @param str 字符串
     * @param mode 1：白色，0：黑色
     */
    void oled_draw_str(const char *str, uint8_t mode);

    /**
     * @brief 在缓存光标处绘制点位图
     *
     * @param bmp：点位图数据，其数据前两个字节必须为点位图的 宽、高
     * @param mode 1：白色，0：黑色
     */
    void oled_draw_bmp(const uint8_t *bmp, uint8_t mode);

    /**
     * @brief 全屏缓存填充指定数据，可用作清屏
     *
     * @param val 0xff：全屏白色，0：全屏黑色
     */
    void oled_fill_full_screen(uint8_t val);

/*******************************************/
/*******************************************/
/*******************************************/
/*******************************************/
// 下列参数请勿改动
// 屏幕参数
#define XLevelL 0x00 // 驱动地址偏移
#define XLevelH 0x10 // 驱动地址偏移
#define YLevel 0xB0  // 驱动地址偏移

#ifdef SSD1306_12832
#define WIDTH 128   // 宽度
#define HEIGHT 32   // 高度
#define PAGE_SIZE 4 // 竖直分辨率/8
#endif

#if (defined SSD1306_12864 || defined SH1106_12864)
#define WIDTH 128   // 宽度
#define HEIGHT 64   // 高度
#define PAGE_SIZE 8 // 竖直分辨率/8
#endif

#ifndef NULL
#define NULL ((void *)0)
#endif

    typedef font_ram_place const unsigned char qf_oled_t;
    typedef struct
    {
        unsigned char val_bmp[32];
        char val_ch[4];
    } BMP_CH_FONT_1616_t;
    typedef font_ram_place const BMP_CH_FONT_1616_t qf_font_t;
#define QF_OLED_BMP_DECLARE(bmp_name) extern font_ram_place const unsigned char bmp_name

#include "QF_OLED_BMP.h"

#endif
#ifdef __cplusplus
}
#endif
#endif
